home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / VideoToolbox 96.06.15 / VideoToolboxSources / Timer.c < prev    next >
C/C++ Source or Header  |  1995-08-13  |  22KB  |  541 lines

  1. /*
  2. Timer.c
  3.  
  4. (NOTE: I just learned of a handy Apple Toolbox routine called Microseconds(),
  5. which appeared with System 7, and is documented in Inside Mac: OS Utilities. 
  6. For most applications Microseconds() will be easier to use than Timer.c,
  7. and just as accurate. See the demo NoiseVBL.c for an example of its use.)
  8.  
  9. Timer.c is interval timer based on Apple's Time Manager. It returns the time, in
  10. seconds, that elapsed between calling StartTimer() and PeekTimerSecs() or
  11. StopTimerSecs(). The timing of a single interval (from StartTimer to PeekTimer,
  12. PeekTimerSecs, StopTimer, or StopTimerSecs) is always very accurate (better than
  13. 1 part in 1000 relative to a good time standard) and a precision of better than
  14. 200 µs. Under System 7, which has the Extended Time Manager, (except on the
  15. Radius Rocket) this accuracy applies to all intervals; measuring splits by
  16. peeking has no effect on the accuracy of subsequent times. Under System 6 (or on
  17. the Radius Rocket even under System 7) each peek will lose a variable amount of
  18. time, compromising accuracy of subsequent times from that timer, due to
  19. documented deficiencies of Apple's old Revised Time Manager. The even older
  20. "Standard" Time Manager is not supported here, and will result in an error
  21. message. Note that you are allowed an unlimited number of timers that each
  22. operate independently. Peeking one timer has no effect on the rest of your
  23. timers.
  24.  
  25.     Timer *NewTimer(void);
  26.     void DisposeTimer(Timer *t);
  27.     void StartTimer(Timer *t);
  28.     double PeekTimerSecs(Timer *t);    // s, with µs precision and no time limit
  29.     double StopTimerSecs(Timer *t);    // s, with µs precision and no time limit
  30.     long PeekTimer(Timer *t);    // µs, up to 36 minutes
  31.     long StopTimer(Timer *t);    // µs, up to 36 minutes
  32.  
  33.     Timer *timer;
  34.     long t;
  35.     
  36.     timer=NewTimer();
  37.     StartTimer(timer);
  38.     do(i=0;i<100;i++);
  39.     t=StopTimer(timer);
  40.     DisposeTimer(timer);
  41.     printf("One hundred iterations takes %ld µs\n",t);
  42.  
  43. The timing result comes in two flavors. StopTimerSecs() and PeekTimerSecs() both
  44. return the time in secs as a double, and can time an interval of essentially
  45. unlimited duration with 200 µs precision. StopTimer() and PeekTimer() return the
  46. time in microseconds (with same 200 µs precision) as a long, which can hold a
  47. time up to 2,147,483,647 µs, which is nearly 36 minutes. I generally prefer
  48. PeekTimerSecs, because it's foolproof (won't overflow), even though it's a bit
  49. slower than PeekTimer. But if you don't have a floating point unit, then
  50. PeekTimerSecs is MUCH slower than PeekTimer.
  51.  
  52. You can have an unlimited number of Timers running at once. The only restriction
  53. is that you must create each Timer, by calling NewTimer() before you use it,
  54. and, obviously, should not use it after calling DisposeTimer().
  55.  
  56. The Time Manager seems to use interrupts at a higher rate than the 10 s interval
  57. I requested here (which would never expire in typical use). I infer this from
  58. the fact that disabling interrupts by SetPriority(7) greatly reduces the timing
  59. value returned by StopTimer(). So don't disable interrupts while you're timing.
  60.  
  61. StopTimer() adds a small offset (about 62 µs on a Mac IIci) to the raw time in
  62. order to return an unbiased estimate of the time from when StartTime was called
  63. to when StopTime() was called. This is the most useful time measure for
  64. measuring the interval between two events (e.g. video frames). Alternatively, if
  65. you wish to measure processing time, then it is more useful to compute the time
  66. from when StartTimer() returns to when StopTimer() is called. For this you
  67. simply subtract the fixed duration of StartTimer(), call-to-return, which is
  68. provided in µs in your Timer structure in the long structure member
  69. "timeToStartTimer".
  70.  
  71.     betweenTime = StopTimer(t) - t->timeToStartTimer;
  72.     
  73. When you use StopTimerSecs() you'd have to divide timeToStartTimer by a
  74. million to convert µs to s. For some purposes this offset is negligibly small.
  75. On my Mac IIci the timeToStartTimer is 240 µs. This offset, and the one
  76. mentioned above are measured automatically the first time you call NewTimer().
  77. Alternatively, instead of having to remember the name of the structure member,
  78. just measure the timeToStartTimer yourself and subtract it from all subsequent
  79. measures:
  80.  
  81.     StartTimer(t);
  82.     s0=StopTimerSec(t);
  83.  
  84. The Timer structures are kept in a linked list so that KillEveryTimer(), which
  85. is placed in the _atexit() queue, will find and kill them when the application
  86. exits.
  87.  
  88. TEST PROGRAM:
  89.  
  90. Measure the same interval in three different ways. All three answers agree, at
  91. least under System 7. Note that timer2 is repeatedly peeked at, while "timer" is
  92. uninterrupted. This checks for any side effects of peeking. (There will be a
  93. substantial loss of time due to peeking if you run without the Extended Time Mgr,
  94. i.e. under System 6 or on Radius Rocket.)
  95.  
  96. void main(void)
  97. {
  98.     double s,s2;
  99.     long ticks,m,m2;
  100.     int i,n=1000;
  101.     Timer *timer,*timer2;
  102.     
  103.     printf("We'll simultaneously measure a single time interval in 3 ways:\n"
  104.         "1. by just starting and stopping the timer\n"
  105.         "2. by starting the timer and repeatedly peeking at it %d times\n"
  106.         "3. by using TickCount(), which runs at 60.15 Hz.\n...\n",n);
  107.     timer=NewTimer();
  108.     timer2=NewTimer();
  109.     Delay(1,&ticks);    // synchronize ourselves to the tick counter for better accuracy.
  110.     StartTimer(timer);
  111.     StartTimer(timer2);
  112.     for(i=0;i<n;i++)s2=PeekTimerSecs(timer2);    // time PeekTimerSecs()
  113.     ticks=TickCount()-ticks;
  114.     s=StopTimerSecs(timer);
  115.     s2=StopTimerSecs(timer2);
  116.     printf("The interval was:\n1. %4.0f ms\n2. %4.0f ms\n3. %4.0f±%.0f ms\n\n"
  117.         ,s*1e3,s2*1e3,1e3*(ticks+0.5)/60.15,1e3*0.5/60.15);
  118.     printf("Each call to PeekTimerSecs() takes %4.0f µs, and loses %4.0f µs.\n"
  119.         ,s*1e6/n,(s-s2)*1e6/n);
  120.     StartTimer(timer);
  121.     StartTimer(timer2);
  122.     for(i=0;i<n;i++)m2=PeekTimer(timer2);    // time PeekTimer()
  123.     m=StopTimer(timer);
  124.     m2=StopTimer(timer2);
  125.     printf("Each call to PeekTimer()     takes %4ld µs, and loses %4ld µs.\n"
  126.         ,m/n,(m-m2)/n);
  127.     DisposeTimer(timer);
  128.     DisposeTimer(timer2);
  129. }
  130.  
  131. ACKNOWLEDGMENT:
  132. David Brainard wrote the first draft of PeekTimer().
  133.  
  134. APPLE DOCUMENTION ERRORS:
  135. There are two errors in the Time Manager chapter of "New Inside
  136. Macintosh: Processes". One of the errors is particularly relevant to the
  137. PowerPC. Both errors are on page 3-8.
  138.  
  139. 1. The manual says, "You should set tmWakeUp and tmReserved to 0 when you first
  140. install an extended Time Manager task." That's correct, but it should go on to
  141. say that "You should set tmReserved to 0 before each and every call to
  142. InsXTime."
  143.  
  144. It took me a long time to discover this. Some computers, e.g. my PowerBook 170,
  145. don't care. But, e.g. the PowerMac 6100, does care. It increments tmReserved
  146. each time a task is installed by InsXTime. If tmReserved reaches 128 then the
  147. Time Manager goes haywire. Conversely, everything runs fine on every Mac I've
  148. tested if tmReserved is zeroed before each call to InsXTime.
  149.  
  150. 2. The manual says "The tmWakeUp field contains the time at which the Time
  151. Manager task specified by tmAddr was last executed (or 0 if it has not yet been
  152. executed)." THIS IS WRONG AND MISLEADING! It should say, instead, "The tmWakeUp
  153. field contains the time at which the Time Manager task specified by tmAddr is
  154. scheduled to be executed (or 0 if it has not yet been scheduled)."
  155.  
  156. RADIUS ROCKET BUG:
  157. This applies to my Radius Rocket in my Mac II using RocketWare 1.5 running under
  158. System 7.1 with System Update.
  159.  
  160. Apparently Radius decided not to implement the Extended Time Manager and only
  161. went so far as diverting the InsXTime trap to InsTime. A minimal fix would be to
  162. at least give honest information by making Gestalt correctly report the Time
  163. Manager version as gestaltRevisedTimeMgr instead of falsely claiming it's
  164. gestaltExtendedTimeMgr. My programs check for this, and I spent a whole day
  165. debugging crashes on my Radius due to this misleading information from Gestalt.
  166.  
  167. According to Gestalt, the Extended Time Manager is present, but in fact the
  168. InsXTime trap is only providing the service expected from the InsTime trap of
  169. the Revised Time Manager. Specifically, in the program below, when we call
  170. InsXTime the second time, with a nonzero tmWakeUp, we are supposed to resume the
  171. previous interval, not start a new one. Since the original interval was 100 s,
  172. there should still be lots of time left in it, and when we call RmvTime for the
  173. last time tmCount should be nonzero. This program fails only on my Radius
  174. Rocket. It runs fine on my Mac II, IIci, IIfx, LC475, Quadra 840av, 
  175. PowerBook 170, and PowerMac.
  176.  
  177. UNDERSTANDING THE CODE OF TIMER.C:
  178. There are a few subtleties to understanding the code. Firstly, you should read
  179. the manual, i.e. the Time Manager chapter of New Inside Mac: Processes. If the
  180. Extended Time Manager is available, PeekTimer takes advantage of its ability to
  181. resume the interrupted interval, so the splits (from PeekTimer and
  182. PeekTimerSecs) won't have any effect on the accuracy of subsequent times.
  183. However, there is a tricky case that requires special handling. If we are very
  184. close to the end of the current interval then the interval might expire before
  185. we have a chance to call PrimeTime(). As explained in the manual on page 3-8
  186. this would amount to an impossible request for a task execution time in the
  187. past. When that happens the Time Mgr changes the the requested time to the
  188. present, which would destroy our synchrony. Therefore PeekTimer checks that
  189. there is at least 10 ms left in the interrupted interval, and, if not, then
  190. advances to the next interval, incrementing the counters appropriately as though
  191. our timer task had been called. This transparently preserves accuracy.
  192.  
  193. HISTORY:
  194. 8/19/92 dgp    based on Time Manager chapter in Inside Mac VI and TimeIt.c, 
  195.     which is now obsolete. I also benefited from examining code by Jonothan Kolodny
  196.     forwarded to me by Thomas Busey.
  197. 8/27/92    dgp    Rewrote everything. Made the interrupt service routine reentrant 
  198.     by eliminating all use of global variables, using only the structure pointed
  199.     to by A1. There can now be an unlimited number of timers active at once.
  200.     Added NewTimer() and DisposeTimer() to manage them. 
  201. 9/10/92    dgp    added calls to VM to HoldMemory() and UnHoldMemory(). According to Apple's
  202.     Memory book this isn't strictly necessary, since Time Manager tasks will be
  203.     called only when it's safe.
  204. 1/11/93 dgp StopTimerSecs() now returns NAN if called with a NULL pointer.
  205. 7/9/93    dgp    Test MATLAB in if() instead of #if. 
  206. 5/28/94 dgp Made compatible with Apple's Universal Headers. Thanks to Bob Dougherty 
  207. (wolfgang@cats.ucsc.edu) for reporting the incompatibility.
  208. 9/7/94    dgp    Made declaration of TimerTask--how it receives the argument ptr--conditional on PowerPC.
  209. 10/22/94 dgp Added PeekTimer, written by David Brainard, and enhanced it to used the Extended
  210. Time Mgr's facility of resuming an interrupted interval. Modularized all the code somewhat,
  211. eliminating duplications.
  212. 10/24/94 dgp Made declaration of GetA0() explicitly indicate that answer is returned in D0, 
  213. for better compatibility with Metrowerks CodeWarrior C.
  214. 10/27/94 dgp It is very annoying for the machine to crash anytime you try to quit your application
  215. by escaping via MacsBugs escape to shell. The problem is that CodeWarrior 4.5 doesn't attach
  216. the atexit() tasks to _EscapeToShell. So I do it here. 
  217. 11/3/94 dgp In response to a bug report by David Brainard that after 128 calls to PeekTimer things
  218. got screwy I replicated the problem and discovered that I could cure it by clearing tmReserved
  219. in ResumeTimer below, though I have no idea why this is necessary.
  220. 11/4/94 dgp After discovering that the Radius Rocket lies, via Gestalt, claiming that
  221. the Extended Time Manager is present when it really isn't, I added code to check for
  222. the services of the Extended Time Manager, and adjust t->timeManagerVersion accordingly.
  223. This eliminates the crashes I was getting when running PeekTimer on the Radius Rocket.
  224. 11/5/94 dgp tested and debugged the code when running without the Extended Time Manager.
  225. 1/5/95    dgp made compatible with Universal Headers 2.
  226. 5/3/95 dgp fixed test for presence of vm, which before was always returning false.
  227. 6/6/95 dgp in response to report from David Brainard, PeekTimer now returns LONG_MIN, like
  228. StopTimer(), when supplied a NULL pointer.
  229. 7/1/95 dgp just call AtExitToShell(), without any conditionals. Special cases, e.g. MATLAB,
  230. are handled in AtExitToShell.c.
  231. */
  232. #include "VideoToolbox.h"
  233. #if GENERATINGPOWERPC
  234.     static pascal void TimerTask(Timer *t);
  235. #else
  236.     static pascal void TimerTask(void);
  237. #endif
  238. void KillEveryTimer(void);
  239.  
  240. /* This is a copy for reference. Original is in VideoToolbox.h.
  241. struct Timer{
  242.     TMTask time;
  243.     long ourA5;
  244.     long interval;                    // interval length (in negative µs)
  245.     long elapsed;                    // extra time, (in -µs), beyond elapsedIntervals*interval
  246.     long elapsedIntervals;            // positive count
  247.     long timeToStartTimer;            // minimum time in µs
  248.     long stopDelay;                    // µs from call to stop, re from call to start
  249.     long timeManagerVersion;
  250.     struct Timer *next,*previous;    // doubly linked list of Timers
  251. };
  252. */
  253.  
  254. static Timer defaultTimer,qTimer;
  255. static long vmPresent=0,pageSize=0;
  256. #define TASK_SIZE 1024    // Generous guess for size of routine
  257. static double TimerSecs(Timer *t);
  258. static void ResumeTimer(Timer *t);
  259. static long TimerMicroSecs(Timer *t);
  260. #if !defined(NewTimerProc)
  261.     #define NewTimerProc(proc)     (TimerProcPtr)proc
  262. #endif
  263.  
  264. Timer *NewTimer(void)
  265. {
  266.     static short firstTime=1;
  267.     Timer *t,*tt;
  268.     long j;
  269.     
  270.     if(firstTime){
  271.         firstTime=0;
  272.         qTimer.next=qTimer.previous=NULL;
  273.         AtExitToShell(KillEveryTimer);
  274.         Gestalt(gestaltVMAttr,&vmPresent);
  275.         vmPresent &= 1L<<gestaltVMPresent;
  276.         if(vmPresent)Gestalt(gestaltLogicalPageSize,&pageSize);
  277.         t=&defaultTimer;
  278.         t->ourA5 = SetCurrentA5();
  279.         t->time.tmAddr = NewTimerProc(TimerTask);
  280.         t->time.tmCount=t->time.tmWakeUp=t->time.tmReserved=0;
  281.         t->elapsedIntervals=t->elapsed=0;                            
  282.         t->timeManagerVersion=0;
  283.         Gestalt(gestaltTimeMgrVersion,&t->timeManagerVersion);
  284.         if(t->timeManagerVersion>=gestaltExtendedTimeMgr){
  285.             // Let's make sure the Extended Time Manager is really here.
  286.             // Under System 7.1 the Radius Rocket claims it's present, but it's not.
  287.             TMTask time;
  288.  
  289.             time.tmAddr = NULL;
  290.             time.tmCount=time.tmWakeUp=time.tmReserved=0;
  291.             InsXTime((QElemPtr)&time);
  292.             PrimeTime((QElemPtr)&time,-100000000L);    // interval of 100 s
  293.             RmvTime((QElemPtr) &time);
  294.             time.tmReserved=0;                        // undocumented, but necessary on some Macs
  295.             InsXTime((QElemPtr)&time);
  296.             PrimeTime((QElemPtr)&time,0);            // resume interrupted interval
  297.             RmvTime((QElemPtr) &time);
  298.             // a tmCount of zero couldn't happen if the Extended Time Manager were present.
  299.             if(time.tmCount==0)t->timeManagerVersion=gestaltRevisedTimeMgr;
  300.         }
  301.         switch(t->timeManagerVersion){
  302.         case 0:
  303.         case gestaltStandardTimeMgr:
  304.             printf("NewTimer: old System lacks the Revised Time Manager. %s\n",__FILE__);
  305.             return NULL;
  306.         case gestaltRevisedTimeMgr:
  307.         case gestaltExtendedTimeMgr:
  308.         default:
  309.             // The programs in Timer.c assume that t->interval<0
  310.             t->interval = -10000000;        // Set the timer interval to 10 s
  311.         }
  312.         t->next=NULL;
  313.         t->previous=&qTimer;
  314.         t->timeToStartTimer=t->stopDelay=0;
  315.         
  316.         // Measure timeToStartTimer and stopDelay offsets.
  317.         t=NewTimer();
  318.         StartTimer(t);
  319.         t->timeToStartTimer=StopTimer(t);
  320.         tt=NewTimer();
  321.         StartTimer(t);
  322.         StartTimer(tt);
  323.         j=StopTimer(t);
  324.         DisposeTimer(tt);
  325.         t->stopDelay=2*t->timeToStartTimer-j;
  326.         // The computed "cycle" interval will have stopDelay removed.
  327.         // The user wishing to compute the "between" interval will be
  328.         // subtracting the timeToStartTimer, so we should subtract 
  329.         // the stopDelay from that so the stopDelay cancels out when 
  330.         // the "between" time is computed.
  331.         t->timeToStartTimer-=t->stopDelay;
  332.         defaultTimer.timeToStartTimer=t->timeToStartTimer;
  333.         defaultTimer.stopDelay=t->stopDelay;
  334.         return t;
  335.     }
  336.     t=(Timer *)NewPtr(sizeof(Timer));
  337.     if(t!=NULL){
  338.         *t=defaultTimer;
  339.         t->next=qTimer.next;
  340.         if(t->next!=NULL)t->next->previous=t;
  341.         qTimer.next=t;
  342.         if(vmPresent){
  343.             HoldMemory(t,sizeof(*t));
  344.             if(t->time.tmAddr!=NULL)HoldMemory(t->time.tmAddr,TASK_SIZE);
  345.         }
  346.         if(t->timeManagerVersion>=gestaltExtendedTimeMgr)InsXTime((QElemPtr)t);
  347.         else InsTime((QElemPtr)t);
  348.     }
  349.     return t;
  350. }
  351.  
  352. void DisposeTimer(Timer *t)
  353. {
  354.     if(t==NULL)return;
  355.     RmvTime((QElemPtr)t);
  356.     if(vmPresent){
  357.         //UnholdMemory(t,sizeof(*t));
  358.         if(qTimer.next==t && t->next==NULL && t->time.tmAddr!=NULL)
  359.             UnholdMemory(t->time.tmAddr,TASK_SIZE);
  360.     }
  361.     t->previous->next=t->next;
  362.     if(t->next!=NULL)t->next->previous=t->previous;
  363.     DisposPtr((Ptr)t);
  364. }
  365.  
  366. void StartTimer(Timer *t)
  367. {
  368.     if(t==NULL)return;
  369.     if(t->time.qType<0)StopTimer(t);    // whoops, it's already active.        
  370.     PrimeTime((QElemPtr)t,t->interval);
  371. }
  372.  
  373. static void ResumeTimer(Timer *t)
  374. {
  375.     // I don't understand why, but it is important to clear tmReserved.
  376.     // The Time Manager documentation only says that tmReserved should be zeroed 
  377.     // when you FIRST install your task, not EACH TIME you install it.
  378.     // At least some computers (e.g. PowerMac) increment tmReserved each time PeekTimer
  379.     // is called, and things get screwy if tmReserved>=128. 
  380.     t->time.tmReserved=0;
  381.     if(t->timeManagerVersion>=gestaltExtendedTimeMgr){
  382.         // The Extended Time Mgr (System 7) allows us to resume exactly where we left off
  383.         // provided we don't clear t->time.tmWakeUp
  384.         if(t->time.tmCount==0 || t->time.tmCount<0 && t->time.tmCount>-10000){
  385.             // we're dangerously close to the end of this interval: skip to next interval
  386.             t->elapsedIntervals++;
  387.             InsXTime((QElemPtr)t);
  388.             PrimeTime((QElemPtr)t,t->interval);        // resume, skipping current interval's wakeup
  389.         }else{
  390.             InsXTime((QElemPtr)t);
  391.             PrimeTime((QElemPtr)t,0);                // resume interrupted interval
  392.         }
  393.     }else{
  394.         // Before System 7 we must start a new interval
  395.         t->elapsed+=t->interval;
  396.         if(t->time.tmCount<0)t->elapsed-=t->time.tmCount;
  397.         else t->elapsed+=t->time.tmCount*1000;
  398.         if(t->elapsed<t->interval){
  399.             // avoid overflow of t->elapsed
  400.             t->elapsed-=t->interval;
  401.             t->elapsedIntervals++;
  402.         }
  403.         t->time.qType=t->time.tmReserved=t->time.tmWakeUp=t->time.tmCount=0;
  404.         InsTime((QElemPtr)t);
  405.         PrimeTime((QElemPtr)t,t->interval);        // resume timing
  406.     }
  407. }
  408.  
  409. static double TimerSecs(register Timer *t)
  410. {
  411.     register double s;
  412.  
  413.     // add up the elapsed time plus the interval we're in, minus the time left in it
  414.     s=-((double)(t->elapsedIntervals+1)*t->interval + t->elapsed);// µs
  415.     if(t->time.tmCount>0) s-=t->time.tmCount*1000;        // -µs until end of interval
  416.     else s+=t->time.tmCount;                            // -µs until end of interval
  417.     s-=t->stopDelay;                                    // compute "cycle" time
  418.     s*=0.000001;                                        // µs to s
  419.     return s;
  420. }
  421.  
  422. static long TimerMicroSecs(register Timer *t)
  423. {
  424.     register long microSecs;
  425.  
  426.     // add up the elapsed time plus the interval we're in, minus the time left in it
  427.     microSecs=-((t->elapsedIntervals+1)*t->interval + t->elapsed);// µs
  428.     if(t->time.tmCount>0) microSecs-=t->time.tmCount*1000;        // -µs until end of interval
  429.     else microSecs+=t->time.tmCount;                            // -µs until end of interval
  430.     microSecs-=t->stopDelay;                                    // compute "cycle" time
  431.     return microSecs;
  432. }
  433.  
  434. double PeekTimerSecs(Timer *t)
  435. {
  436.     Timer tCopy,*tt=&tCopy;
  437.  
  438.     if(t==NULL)return NAN;
  439.     RmvTime((QElemPtr) t);
  440.     *tt=*t;    // get the info and resume timing as quickly as possible
  441.     ResumeTimer(t);
  442.     return TimerSecs(tt);
  443. }
  444.  
  445. long PeekTimer(Timer *t)
  446. {
  447.     Timer tCopy,*tt=&tCopy;
  448.  
  449.     if(t==NULL)return LONG_MIN;
  450.     RmvTime((QElemPtr) t);
  451.     *tt=*t;    // get the info and resume timing as quickly as possible
  452.     ResumeTimer(t);
  453.     return TimerMicroSecs(tt);
  454. }
  455.  
  456. long StopTimer(Timer *t)                            // Returns µs
  457. {
  458.     long microSecs;
  459.  
  460.     if(t==NULL)return LONG_MIN;
  461.     RmvTime((QElemPtr)t);
  462.     microSecs=TimerMicroSecs(t);
  463.     
  464.     // Reinstall the Timer, to be ready for for another call to StartTimer()
  465.     t->time.qType=t->time.tmCount=t->time.tmWakeUp=t->time.tmReserved=0;
  466.     t->elapsed=t->elapsedIntervals=0;                            
  467.     if(t->timeManagerVersion>=gestaltExtendedTimeMgr)InsXTime((QElemPtr)t);
  468.     else InsTime((QElemPtr)t);
  469.     
  470.     return microSecs;
  471. }
  472.  
  473. double StopTimerSecs(Timer *t)                        // Returns secs
  474. {
  475.     double s;
  476.  
  477.     if(t==NULL)return NAN;
  478.     RmvTime((QElemPtr)t);
  479.     s=TimerSecs(t);
  480.     
  481.     // Reinstall the Timer, to be ready for for another call to StartTimer()
  482.     t->time.qType=t->time.tmCount=t->time.tmWakeUp=t->time.tmReserved=0;
  483.     t->elapsed=t->elapsedIntervals=0;                            
  484.     if(t->timeManagerVersion>=gestaltExtendedTimeMgr)InsXTime((QElemPtr)t);
  485.     else InsTime((QElemPtr)t);
  486.     
  487.     return s;
  488. }
  489.  
  490. // KillEveryTimer turns off all our Timers before we quit. There is no need to
  491. // free the space since the system will do that automatically when the application terminates.
  492.  
  493. void KillEveryTimer(void)
  494. {
  495.     Timer *t;
  496.     extern Timer qTimer;
  497.  
  498.     t=qTimer.next;
  499.     while(t!=NULL){
  500.         RmvTime((QElemPtr)t);
  501.         if(vmPresent){
  502.             UnholdMemory(t,sizeof(*t));
  503.             if(t->time.tmAddr!=NULL)UnholdMemory(t->time.tmAddr,TASK_SIZE);
  504.         }
  505.         t=t->next;
  506.     }
  507. }
  508.  
  509. // On 68k machines the Revised & Extended Time managers set A1=&task.time before calling TimerTask
  510. // The code allowing access to globals is commented out because it is not needed here.
  511.  
  512. #if (THINK_C || THINK_CPLUS || SYMANTEC_C)
  513.     #pragma options(!profile)    // it would be dangerous to call the profiler from here
  514. #endif
  515. #if __MWERKS__
  516.     #pragma profile off            // on 68k it would be dangerous to call the profiler from here
  517. #endif
  518. #if GENERATING68K
  519.     #pragma parameter __D0 GetA1
  520.     long GetA1(void)=0x2009;    // MOVE.L A1,D0
  521. #endif
  522.  
  523. #if GENERATINGPOWERPC
  524.     static pascal void TimerTask(Timer *t)
  525. #else
  526.     static pascal void TimerTask(void)            // Called at interrupt time
  527. #endif
  528.     {
  529.     #if GENERATING68K
  530.         //    long oldA5;
  531.         Timer *t;
  532.         t=(Timer *)GetA1();
  533.         //    oldA5 = SetA5(t->ourA5);            // Reestablish A5 for global variables
  534.     #endif
  535.     PrimeTime((QElemPtr)t,t->interval);            // Repeat the interval
  536.     t->elapsedIntervals++;
  537.     #if GENERATING68K
  538.         //    SetA5(oldA5);                         // Restore A5
  539.     #endif
  540. }
  541.